ASP.NTE Core 下的Middleware是如何運作的
Middleware 中文叫做中介軟體
是一種用來溝通應用端程式以及資料端程式的橋梁
依照各種不同的功能面會有不同的 Middleware
各司其職,有效分工去增加效率
ASP .NET Core 使用 Middleware 去執行個個狀態所要做的事情:
利用組件 component 的方式透過 pipeline 執行下一個 Middleware ,也可以透過next()方法直接執行下一個 Middleware ,而非依序執行
所謂的 Middleware 其實就是一個 class ,可以在這些物件裡添加邏輯, ASP .NET Core 已經有提供現有的Middleware提供我們使用
透過 pipeline(管線)的方式去執行,可以把他想成一座工廠,依照每個產線的流程依序去完成產品的製作,另外在 pipeline 的模式理會符合先進後出原則
舉個例子
還記得以前小時候在麥當勞打工的時候
櫃檯的要求是一個餐點必須在1分鐘完成
當時的流程大概是
點餐-> 壓飲料->拿漢堡>拿薯條>拿飲料>包裝結帳
(題外話這是早期的版本,後來改成現點現做,採用單一職責分工,不是都由一個服務生完成)
可以看到其實這就是先進後出的概念
點餐 - 包裝結帳
壓飲料 - 拿飲料
拿漢堡&拿薯條
回到 .NET Core的例子
在上篇提到的Startup.cs裡面的Configure方法
裡面會依序執行Middleware
也可以透過一些方法去操作Middleware的行為,例如next()
使用 Cli 建立的 MVC 預設專案範本,會自動寫入幾項目前微軟提供的 Middleware 在 Startup.cs 裡面,這些 Middleware 都已經先封裝成一個靜態方法,並已UseXXXX命名,另外也可以在 UseRouting() 和 UseEndpoint() 裡新增一些客制的 Middleware
ASP .NET Core 的 IApplicationBuilder 是用來處理 pipeline 運作
其實就是一連串的HttpContext 去做request 和 response 的操作
其中也提供幾個方法,可以用來註冊各個 Middleware
此部分會實作兩個例子,使用 IApplicationBuilder 方法去操作 Middleware 以及封裝 Middleware
使用一開始提到麥當勞的例子,示範的程式碼如下
前面有提到 Use 可以串接下一個 Middleware,在這裡先來看看 Use 的語法
輸入一個泛型的委派,第一個參數是 httpContext context,可以接收 Http 的 request 和 reaponse ,第二個參數是一個 function ,用來指向下一個 Middleware 的方法 next()
Use()
//order Middleware at counter
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Welcome to McDonald's, Would you like to order? \n");
await context.Response.WriteAsync("You order a Big Mac meal, it's 9.65 SGD \n");
await next();
await context.Response.WriteAsync("here is your receipt and meal, Thank you for coming.\n");
});
至於 Run() 這個方法,就只有傳入一個 httpContext context
透過委派的方式去傳遞,不會串連各個 Middleware
Run()
app.Run(async (context) =>
{
await context.Response.WriteAsync("Preparing and paking fries \n");
});
執行結果
在範本裡面微軟提供的 Middleware
大部分都已經封裝成一個類別,這樣也比較易於管理且程式碼也較簡潔
在這邊新增一個 DrinkMiddleware.cs 去封裝飲料的 Midleware
根據 VS2019 可以直接在專案新增一個Middleware的 cs
新增後將 Invoke 方法改成一個非同步的方法
將原先飲料部分的邏輯加入到這個方法裡面
startup.cs
//drink bar
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Making a drink \n");
await next();
await context.Response.WriteAsync("Taking a drink \n");
});
將這段調整到Invoke方法內
DrinkMiddleware.cs
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class DrinkMiddleware
{
private readonly RequestDelegate _next;
public DrinkMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
await httpContext.Response.WriteAsync("Making a drink \n");
await _next(httpContext);
await httpContext.Response.WriteAsync("Taking a drink \n");
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class DrinkMiddlewareExtensions
{
//UseDrinkMiddleware 是自己定義的,也就是等等app所呼叫的方法
public static IApplicationBuilder UseDrinkMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<DrinkMiddleware>();
}
}
如此一來在 startup.cs 就可以直接使用這個類別
這樣也可以在擴充自己要增加的邏輯
此產生的結果和前一個顯示是相同的
參考資料:
https://zhuanlan.zhihu.com/p/112507925
https://aregcode.com/blog/2019/dotnetcore-understanding-aspnet-endpoint-routing/